package spynx.desl.runtime;

import java.util.HashMap;
import java.util.Map;

import spynx.desl.runtime.operators.impl.AddOperator;
import spynx.desl.runtime.operators.impl.ArrayOperator;
import spynx.desl.runtime.operators.impl.AssignOperator;
import spynx.desl.runtime.operators.impl.BooleanOperator;
import spynx.desl.runtime.operators.impl.ComparisonOperator;
import spynx.desl.runtime.operators.impl.DotResolutionOperator;
import spynx.desl.runtime.operators.impl.ExecuteOperator;
import spynx.desl.runtime.operators.impl.IndexOperator;
import spynx.desl.runtime.operators.impl.InvocationOperator;
import spynx.desl.runtime.operators.impl.JobOperator;
import spynx.desl.runtime.operators.impl.NameResolverOperator;
import spynx.desl.runtime.operators.impl.NumericOperator;
import spynx.desl.runtime.operators.impl.RefOperator;
import spynx.desl.runtime.operators.impl.SelectOperator;

public class OperatorRegistry {
	private Map<String, IOperator> operators;
	
	public static OperatorRegistry INSTANCE = new OperatorRegistry(); 
	
	private OperatorRegistry() {
		operators = new HashMap<String, IOperator>();
		
		registerOperator(new DotResolutionOperator());
		registerOperator(new AddOperator());
		registerOperator(new AssignOperator());
		registerOperator(new InvocationOperator());
		registerOperator(new ArrayOperator());
		registerOperator(new IndexOperator());
		registerOperator(new JobOperator());
		registerOperator(new RefOperator());
		registerOperator(new NameResolverOperator());
		registerOperator(new SelectOperator());
		
		// null operator
		registerOperator(new IOperator() {
			
			@Override
			public String getOperator() {
				return "null";
			}
			
			@Override
			public Object apply(IExecutionContext context, String operator,
					Object... args) {
				return null;
			}
		});
		
		ExecuteOperator execOp = new ExecuteOperator();
		registerOperator("evaluate", execOp);
		registerOperator("execute", execOp);
		
		NumericOperator numOp = new NumericOperator();
		registerOperator("-", numOp);
		registerOperator("*", numOp);
		registerOperator("/", numOp);
		registerOperator("<<", numOp);
		registerOperator(">>", numOp);
		registerOperator("|", numOp);
		registerOperator("&", numOp);
		registerOperator("%", numOp);
		
		ComparisonOperator compOp = new ComparisonOperator();
		registerOperator(">", compOp);
		registerOperator(">=", compOp);
		registerOperator("<", compOp);
		registerOperator("<=", compOp);
		registerOperator("==", compOp);
		registerOperator("!=", compOp);
		
		BooleanOperator boolOp = new BooleanOperator();
		registerOperator("&&", boolOp);
		registerOperator("||", boolOp);
	}
	
	public void registerOperator(String operator, IOperator op) {
		operators.put(operator, op);
	}
	
	public void registerOperator(IOperator op) {
		operators.put(op.getOperator(), op);
	}
	
	public IOperator getOperator(String operator) {
		return operators.get(operator);
	}
	
	@SuppressWarnings("unchecked")
	public <T> T applyOperator(String operator, Object...args) {
		IOperator op = getOperator(operator);
		if (op != null)
			return (T)op.apply(null, operator, args);
		throw new RuntimeException("Unknown operator: " + operator);
	}
}
